package cz.tomasdvorak.cars.dao.impl;

import cz.tomasdvorak.cars.dao.UsersDao;
import cz.tomasdvorak.cars.dto.Role;
import cz.tomasdvorak.cars.dto.User;
import cz.tomasdvorak.cars.exceptions.RegistrationFailedException;
import cz.tomasdvorak.cars.utils.Hashing;
import cz.tomasdvorak.cars.auth.MyAuthenticatedWebSession;
import org.apache.log4j.Logger;
import org.apache.wicket.Session;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

// TODO: remove table names from code and move to config xml
public class UsersDaoImpl implements UsersDao {

    private static final Logger log = Logger.getLogger(UsersDaoImpl.class);

    private DataSource dataSource;
    private SimpleJdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        jdbcTemplate = new SimpleJdbcTemplate(dataSource);
    }

    /* DAO methods go here */

    private final RowMapper<User> USER_MAPPER = new RowMapper<User>() {
        public User mapRow(ResultSet rs, int i) throws SQLException {
            String roleString = rs.getString("role");
            Role role = null;
            if(roleString != null && !roleString.trim().isEmpty()) {
                role = Role.valueOf(roleString);
            }
            return new User(rs.getInt("id"), rs.getString("name"), role);
        }
    };

    public List<User> getAllUSers() {
        return jdbcTemplate.query("SELECT * FROM tdvorak_user", USER_MAPPER);
    }

    @Transactional
    public void registerNewUser(String name, String password) throws RegistrationFailedException {
        // TODO: check duplicate usernames and block duplicates
        try {
            jdbcTemplate.update("INSERT INTO tdvorak_user (name, password, role) VALUES(?, ?, ?)", name,
                    computePassword(password), Role.USER.name());
        } catch (Exception e) {
            throw new RegistrationFailedException("Failed registring user", e);
        }
    }

    private String computePassword(String password) {
        // TODO: hashing with salt, different for every user
        return Hashing.sha1(password);
    }

    public User authenticate(String username, String password) {
        try {
        return jdbcTemplate.queryForObject("SELECT * FROM tdvorak_user WHERE name=? AND password=? LIMIT 1",
                USER_MAPPER, username, computePassword(password));
        } catch (EmptyResultDataAccessException e) {
            log.warn("Illegal login detected: " + username);
        }
        return null;
    }

    public void changePassword(User user, String oldPassword, String newPassword)
            throws IllegalAccessException {
        MyAuthenticatedWebSession session = (MyAuthenticatedWebSession) Session.get();
        if (session.getUser() != null && (session.getUser().equals(user) || session.getUser()
                .isAdmin())) {
            if (!user.isAdmin()) {
                User authenticated = authenticate(user.getName(), oldPassword);
                if (authenticated == null) {
                    throw new IllegalAccessException(
                            "Failed to authenticate user by given old password");
                }
            }
            jdbcTemplate.update("UPDATE tdvorak_user SET password=? WHERE id=?", computePassword(
                    newPassword), user.getId());
        } else {
            throw new IllegalAccessException();
        }
    }

    public User getUserById(int id) {
        try {
            return jdbcTemplate.queryForObject("SELECT * FROM tdvorak_user WHERE id=?", USER_MAPPER, id);
        } catch (EmptyResultDataAccessException e) {
            log.error(e);
        }
        return null;
    }
}
